/*************************************************************************
	> File Name: isl29023.c
	> Author: shuixianbing
	> Mail: shui6666@126.com 
	> Created Time: 2017年03月06日 星期一 15时57分29秒
 ************************************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/gpio.h>
#include <mach/sys_config.h>

#define ISL29023_NAME "isl29023"

#define GPIO_INT GPIOG(13) 

#define ISL29023_COMMAND1 0x00
#define ISL29023_MODE_SHIFT (5)
#define ISL29023_MODE_MASK (BIT(7)|BIT(6)|BIT(5))
#define ISL29023_INT_FLAG_SHIFT (2)
#define ISL29023_INT_FLAG_MASK (BIT(2))
#define ISL29023_INT_PERSISTS_SHIFT (0)
#define ISL29023_INT_PERSISTS_MASK (BIT(0)|BIT(1))

#define ISL29023_COMMAND2 0x01
#define ISL29023_RES_SHIFT (2)
#define ISL29023_RES_MASK (BIT(3)|BIT(2))
#define ISL29023_RANGE_SHIFT (0)
#define ISL29023_RANGE_MASK (BIT(0)|BIT(1))

#define ISL29023_REG_LSB_SENSOR 0x02
#define ISL29023_REG_MSB_SENSOR 0x03

#define ISL29023_REG_IRQ_TH_LO_LSB 0x04
#define ISL29023_REG_IRQ_TH_LO_MSB 0x05
#define ISL29023_REG_IRQ_TH_HI_LSB 0x06
#define ISL29023_REG_IRQ_TH_HI_MSB 0x07

#define ISL29023_NUM_CACHABLE_REGS 8
#define DEF_RANGE 2

#define ISL29023_PD_MODE 0x0
#define ISL29023_ALS_ONCE_MODE 0x1
#define ISL29023_IR_ONCE_MODE 0x2
#define ISL29023_ALS_CONT_MODE 0x5
#define isl29023_IR_CONT_MODE 0x6

#define ISL29023_INT_PERSISTS_1  0x0
#define ISL29023_INT_PERSISTS_4  0x1
#define ISL29023_INT_PERSISTS_8  0x2
#define ISL29023_INT_PERSISTS_16 0x3

#define ISL29023_RES_16 0x0
#define ISL29023_RES_12 0x1
#define ISL29023_RES_8  0x2
#define ISL29023_RES_4  0x3

#define ISL29023_RANGE_1K  0x0 
#define ISL29023_RANGE_4K  0x1 
#define ISL29023_RANGE_16K 0x2
#define ISL29023_RANGE_64K 0x3


static const unsigned short normal_i2c[2] = {0x44, I2C_CLIENT_END};
static struct i2c_device_id isl29023_id[] = {
	{ISL29023_NAME, 0},
	{}
};

struct isl29023_data {
	struct i2c_client *client;
	struct mutex lock;
	struct input_dev *input;
	struct work_struct work;
	struct workqueue_struct *workqueue;
	char phys[32];
	u8 reg_cache[ISL29023_NUM_CACHABLE_REGS];
	u8 mode_before_suspend;
	u8 mode_before_interrupt;
	u16 rext;
};

static int gain_range[] = {
	1000, 4000, 16000, 64000
};

static int __isl29023_read_reg(struct i2c_client *client, u32 reg, u8 mask, u8 shift){
	struct isl29023_data *data = i2c_get_clientdata(client);
	return (data->reg_cache[reg]&mask) >> shift;
}

static int __isl29023_write_reg(struct i2c_client *client, u32 reg, u8 mask,
			u8 shift, u8 val){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int ret = 0;
	u8 tmp;
	
	if(reg >= ISL29023_NUM_CACHABLE_REGS)
	  return -EINVAL;

	mutex_lock(&data->lock);

	tmp = data->reg_cache[reg];
	tmp &= ~mask;
	tmp |= val << shift;

	ret = i2c_smbus_write_byte_data(client, reg, tmp);
	if(!ret)
	  data->reg_cache[reg] = tmp;

	mutex_unlock(&data->lock);

	return ret;
}

//internally used functions

//interrupt persists
static int isl29023_get_int_persists(struct i2c_client *client){
	return __isl29023_read_reg(client, ISL29023_COMMAND1, ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT);
}

static int isl29023_set_int_persists(struct i2c_client *client, int int_persists){
	return __isl29023_write_reg(client, ISL29023_COMMAND1,ISL29023_INT_PERSISTS_MASK,ISL29023_INT_PERSISTS_SHIFT,int_persists);
}

//interrupt flag
static int isl29023_get_int_flag(struct i2c_client *client){
	return __isl29023_read_reg(client, ISL29023_COMMAND1,ISL29023_INT_FLAG_MASK,ISL29023_INT_FLAG_SHIFT);
}

static int isl29023_set_int_flag(struct i2c_client *client, int flag){
	printk("bshui isl29023_set int flag %d\n", flag);
	return __isl29023_write_reg(client, ISL29023_COMMAND1,ISL29023_INT_FLAG_MASK,ISL29023_INT_FLAG_SHIFT, flag);
}

//interrupt lt
static int isl29023_get_int_lt(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int lsb, msb, lt;
	
	mutex_lock(&data->lock);
	lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB);
	if(lsb < 0){
		mutex_unlock(&data->lock);
		return lsb;
	}

	msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB);
	mutex_unlock(&data->lock);
	if(msb < 0)
	  return msb;

	lt = ((msb<<8) | lsb);

	return lt;
}

static int isl29023_set_int_lt(struct i2c_client *client, int lt){
	int ret = 0;
	struct isl29023_data *data = i2c_get_clientdata(client);

	mutex_lock(&data->lock);
	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB, lt&0xff);
	
	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB, 
				(lt >> 8)&0xff);
	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = (lt>>8)&0xff;
	data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lt &0xff;
	mutex_unlock(&data->lock);

	return ret;
}
//interrupt ht
static int isl29023_get_int_ht(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int lsb, msb, ht;

	mutex_lock(&data->lock);
	lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB);
	if(lsb < 0){
		mutex_unlock(&data->lock);
		return lsb;
	}

	msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB);
	mutex_unlock(&data->lock);

	if(msb < 0)
	  return msb;

	ht = ((msb << 8) | lsb);

	return ht;
}

static int isl29023_set_int_ht(struct i2c_client *client, int ht){
	int ret = 0;
	struct isl29023_data *data = i2c_get_clientdata(client);

	mutex_lock(&data->lock);
	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB,
				ht&0xff);
	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB,
				(ht >> 8)&0xff);
	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = (ht >> 8)&0xff;
	data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = ht &0xff;
	mutex_unlock(&data->lock);

	return ret;
}

//range
static int isl29023_get_range(struct i2c_client *client){
	return __isl29023_read_reg(client, ISL29023_COMMAND2, ISL29023_RANGE_MASK,
				ISL29023_RANGE_SHIFT);
}

static int isl29023_set_range(struct i2c_client *client, int range){
	return __isl29023_write_reg(client, ISL29023_COMMAND2,
				ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT, range);
}

//resolution
static int isl29023_get_resolution(struct i2c_client *client){
	return __isl29023_read_reg(client, ISL29023_COMMAND2,
				ISL29023_RES_MASK, ISL29023_RES_SHIFT);
}

static int isl29023_set_resolution(struct i2c_client *client, int res){
	return __isl29023_write_reg(client, ISL29023_COMMAND2,
				ISL29023_RES_MASK, ISL29023_RES_SHIFT, res);
}

//mode
static int isl29023_get_mode(struct i2c_client *client){
	return __isl29023_read_reg(client, ISL29023_COMMAND1,
				ISL29023_MODE_MASK, ISL29023_MODE_SHIFT);
}

static int isl29023_set_mode(struct i2c_client *client, int mode){
	printk("bshui isl29023_set_mode %d\n", mode);
	return __isl29023_write_reg(client, ISL29023_COMMAND1, ISL29023_MODE_MASK,
				ISL29023_MODE_SHIFT, mode);
}

//power state
static int isl29023_set_power_state(struct i2c_client *client, int state){
	return __isl29023_write_reg(client, ISL29023_COMMAND1, ISL29023_MODE_MASK,
				ISL29023_MODE_SHIFT, state ? ISL29023_ALS_CONT_MODE : ISL29023_PD_MODE);
}

static int isl29023_get_power_state(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	u8 cmdreg = data->reg_cache[ISL29023_COMMAND1];

	if(cmdreg & ISL29023_MODE_MASK)
	  return 1;
	else
	  return 0;
}

static int isl29023_get_adc_value(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int lsb, msb, range, bitdepth;

	mutex_lock(&data->lock);
	lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_LSB_SENSOR);
	if(lsb < 0){
		mutex_unlock(&data->lock);
		return lsb;
	}

	msb = i2c_smbus_read_byte_data(client, ISL29023_REG_MSB_SENSOR);
	mutex_unlock(&data->lock);

	if(msb < 0)
	  return msb;

	range = isl29023_get_range(client);
	bitdepth = (4 - isl29023_get_resolution(client))*4;

	return (((msb << 8) | lsb) * ((gain_range[range]*499)/data->rext)) >> bitdepth;

}

static int isl29023_get_int_lt_value(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int lsb,msb,range,bitdepth;

	mutex_lock(&data->lock);
	lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB);

	if(lsb < 0){
		mutex_unlock(&data->lock);
		return lsb;
	}

	msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB);
	mutex_unlock(&data->lock);

	if(msb < 0)
	  return msb;

	range = isl29023_get_range(client);
	bitdepth = (4-isl29023_get_resolution(client))*4;

	return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) >> bitdepth;

}

static int isl29023_get_int_ht_value(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int lsb,msb,range,bitdepth;

	mutex_lock(&data->lock);
	lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB);

	if(lsb < 0){
		mutex_unlock(&data->lock);
		return lsb;
	}

	msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB);
	mutex_unlock(&data->lock);

	if(msb < 0)
	  return msb;

	range = isl29023_get_range(client);
	bitdepth = (4 - isl29023_get_resolution(client))*4;

	return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) >> bitdepth;

}

//sysfs layer

//interrupt persists
static ssize_t isl29023_show_int_persists(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);

	printk("bshui %s\n", __func__);
	return sprintf(buf, "%i\n", isl29023_get_int_persists(client));
}

static ssize_t isl29023_store_int_persists(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 10, &val) < 0) || (val >ISL29023_INT_PERSISTS_16))
	  return -EINVAL;

	ret = isl29023_set_int_persists(client, val);
	if(ret < 0)
	  return ret;

	printk("bshui %s\n", __func__);
	return count;
}


static DEVICE_ATTR(int_persists, S_IWUSR | S_IRUGO, isl29023_show_int_persists,
			isl29023_store_int_persists);

// interrupt flag
static ssize_t isl29023_show_int_flag(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);

	return sprintf(buf, "%i\n", isl29023_get_int_flag(client));
}

static ssize_t isl29023_store_int_flag(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 10, &val) < 0) || (val > 1))
	  return -EINVAL;

	ret = isl29023_set_int_flag(client, val);
	if(ret < 0)
	  return ret;

	return count;
}

static DEVICE_ATTR(int_flag, S_IWUSR | S_IRUGO, isl29023_show_int_flag, isl29023_store_int_flag);

//interrupt lt
static ssize_t isl29023_show_int_lt(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);

	return sprintf(buf, "%i\n", isl29023_get_int_lt(client));
}

static ssize_t isl29023_store_int_lt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 16, &val) < 0) || (val > 0xffff))
	  return -EINVAL;

	ret = isl29023_set_int_lt(client, val);
	if(ret < 0)
	  return ret;

	return count;
}

static DEVICE_ATTR(int_lt, S_IWUSR | S_IRUGO, isl29023_show_int_lt, isl29023_store_int_lt);

//interrupt ht
static ssize_t isl29023_show_int_ht(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	return sprintf(buf, "%i\n", isl29023_get_int_ht(client));
}

static ssize_t isl29023_store_int_ht(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 16, &val)< 0) || (val > 0xffff))
	  return -EINVAL;

	ret = isl29023_set_int_ht(client, val);
	if(ret < 0)
	  return ret;

	return count;
}


static DEVICE_ATTR(int_ht, S_IWUSR | S_IRUGO, isl29023_show_int_ht, isl29023_store_int_ht);

//range
static ssize_t isl29023_show_range(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	return sprintf(buf, "%i\n", isl29023_get_range(client));
}

static ssize_t isl29023_store_range(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 10, &val) < 0) || (val > ISL29023_RANGE_64K))
	  return -EINVAL;

	ret = isl29023_set_range(client, val);
	if(ret < 0)
	  return ret;

	return count;
}

static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, isl29023_show_range, isl29023_store_range);

//resolution
static ssize_t isl29023_show_resolution(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	return sprintf(buf, "%d\n", isl29023_get_resolution(client));
}

static ssize_t isl29023_store_resolution(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 10, &val) < 0) || (val > ISL29023_RES_4))
	  return -EINVAL;

	ret = isl29023_set_resolution(client, val);
	if(ret < 0)
	  return ret;

	return count;
}

static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, isl29023_show_resolution, isl29023_store_resolution);

//mode 
static ssize_t isl29023_show_mode(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	return sprintf(buf, "%d\n", isl29023_get_mode(client));
}

static ssize_t isl29023_store_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 10, &val) < 0) || (val > isl29023_IR_CONT_MODE))
	  return -EINVAL;

	ret = isl29023_set_mode(client, val);
	if(ret < 0)
	  return ret;

	return count;
}

static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, isl29023_show_mode, isl29023_store_mode);

//power state
static ssize_t isl29023_show_power_state(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);

	return sprintf(buf, "%d\n", isl29023_get_power_state(client));
}

static ssize_t isl29023_store_power_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	unsigned long val;
	int ret;

	if((strict_strtoul(buf, 10, &val) < 0) || (val > 1))
	  return -EINVAL;

	ret = isl29023_set_power_state(client, val);
	return ret ? ret : count;
}

static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, isl29023_show_power_state, isl29023_store_power_state);

//lux
static ssize_t isl29023_show_lux(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	//No lux data if not operational
	if(!isl29023_get_power_state(client))
	  return -EBUSY;

	return sprintf(buf, "%d\n", isl29023_get_adc_value(client));
}

static DEVICE_ATTR(lux, S_IRUGO, isl29023_show_lux, NULL);
//lux interrupt low threshold
static ssize_t isl29023_show_int_lt_lux(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	//No LUX data if not operational
	if(isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && 
	   isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
	  return -EIO;

	return sprintf(buf, "%d\n", isl29023_get_int_lt_value(client));
}

static ssize_t isl29023_store_int_lt_lux(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	struct isl29023_data *data = i2c_get_clientdata(client);
	unsigned long val, lux_data;
	int range, bitdepth, ret;
	u8 lsb, msb;

	if((strict_strtoul(buf, 10, &val) < 0))
	  return -EINVAL;

	//No LUX data if not operational
	if(isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && 
	   isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
	  return -EIO;

	if(val > (gain_range[isl29023_get_range(client)]*499/data->rext))
	  return -EINVAL;

	range = isl29023_get_range(client);
	bitdepth = (4 - isl29023_get_resolution(client)) * 4;
	lux_data = ((unsigned long)(val << bitdepth)) /((gain_range[range]*499)/data->rext);
	lux_data &= 0xffff;

	msb = lux_data >> 8;
	lsb = lux_data & 0xff;

	mutex_lock(&data->lock);
	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB, lsb);

	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB, msb);
	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = msb;
	data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lsb;
	mutex_unlock(&data->lock);

	return count;
}

static DEVICE_ATTR(int_lt_lux, S_IWUSR | S_IRUGO, isl29023_show_int_lt_lux, isl29023_store_int_lt_lux);
//lux interrupt high threshold

static ssize_t isl29023_show_int_ht_lux(struct device *dev, struct device_attribute *attr, char *buf){
	struct i2c_client *client = to_i2c_client(dev);
	//No LUX data if not operational
	if(isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && 
	   isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
	  return -EIO;

	return sprintf(buf, "%d\n", isl29023_get_int_ht_value(client));
}

static ssize_t isl29023_store_int_ht_lux(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
	struct i2c_client *client = to_i2c_client(dev);
	struct isl29023_data *data = i2c_get_clientdata(client);
	unsigned long val, lux_data;
	int range, bitdepth, ret;
	u8 lsb, msb;

	if((strict_strtoul(buf, 10, &val) < 0))
	  return -EINVAL;

	//No LUX data if not operational
	if(isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && 
	   isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE)
	  return -EIO;

	if(val > (gain_range[isl29023_get_range(client)]*499/data->rext))
	  return -EINVAL;

	range = isl29023_get_range(client);
	bitdepth = (4 - isl29023_get_resolution(client)) * 4;
	lux_data = ((unsigned long)(val << bitdepth)) /((gain_range[range]*499)/data->rext);
	lux_data &= 0xffff;

	msb = lux_data >> 8;
	lsb = lux_data & 0xff;

	mutex_lock(&data->lock);
	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB, lsb);

	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB, msb);
	if(ret < 0){
		mutex_unlock(&data->lock);
		return ret;
	}

	data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = msb;
	data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = lsb;
	mutex_unlock(&data->lock);

	return count;
}

static DEVICE_ATTR(int_ht_lux, S_IWUSR | S_IRUGO, isl29023_show_int_ht_lux, isl29023_store_int_ht_lux);

static struct attribute *isl29023_attributes[] = {
	&dev_attr_int_persists.attr,
	&dev_attr_int_flag.attr,
	&dev_attr_int_lt.attr,
	&dev_attr_int_ht.attr,
	&dev_attr_range.attr,
	&dev_attr_resolution.attr,
	&dev_attr_mode.attr,
	&dev_attr_power_state.attr,
	&dev_attr_lux.attr,
	&dev_attr_int_lt_lux.attr,
	&dev_attr_int_ht_lux.attr,
	NULL
};

static const struct attribute_group isl29023_attr_group = {
	.attrs = isl29023_attributes,
};

static int isl29023_init_client(struct i2c_client *client){
	struct isl29023_data *data = i2c_get_clientdata(client);
	int i;
	//read all the registers once to fill the cache.
	//if one of the reads fails, we consider the init failed;
	for(i=0; i< ARRAY_SIZE(data->reg_cache); i++){
		int v = i2c_smbus_read_byte_data(client, i);
		if(v < 0)
		  return -ENODEV;

		data->reg_cache[i] = v;
	}

	//set defaults
	isl29023_set_int_persists(client, ISL29023_INT_PERSISTS_8);
	isl29023_set_int_ht(client, 0xffff);
	isl29023_set_int_lt(client, 0x0);
	isl29023_set_range(client, ISL29023_RANGE_16K);
	isl29023_set_resolution(client, ISL29023_RES_16);
	isl29023_set_mode(client, ISL29023_ALS_CONT_MODE);
	isl29023_set_int_flag(client, 1);
	isl29023_set_power_state(client, 1);

	return 0;
}

static void isl29023_work(struct work_struct *work){

	printk("bshui isl29023 work----\n");
}

static irqreturn_t isl29023_irq_handler(int irq, void *handle){
	//struct isl29023_data *data = handle;
	printk("bshui isl29023_irq_handler-----\n");

	return IRQ_HANDLED;
}

static int __devinit isl29023_probe(struct i2c_client *client, const struct i2c_device_id *id){

	struct isl29023_data *data;
	struct input_dev *input_dev;
	int err = 0;


	data = kzalloc(sizeof(struct isl29023_data), GFP_KERNEL);
	if(!data)
	  return -ENOMEM;

	data->client = client;
	data->rext = 499; //499k Rext registor
	snprintf(data->phys, sizeof(data->phys), "%s", dev_name(&client->dev));

	i2c_set_clientdata(client, data);
	mutex_init(&data->lock);

	//initialize the isl29023 chip
	err = isl29023_init_client(client);
	if(err)
	  goto exit_kfree;

	//register sysfs
	err = sysfs_create_group(&client->dev.kobj, &isl29023_attr_group);
	if(err)
	  goto exit_kfree;

	input_dev = input_allocate_device();
	if(!input_dev){
		err = -ENOMEM;
		goto exit_kfree;
	}

	data->input = input_dev;
	input_dev->name = "isl29023 light snesor";
	input_dev->id.bustype = BUS_I2C;
	input_dev->phys = data->phys;

	__set_bit(EV_ABS, input_dev->evbit);
	input_set_abs_params(input_dev, ABS_MISC, 0, gain_range[DEF_RANGE]*499/data->rext, 0, 0);

	err = input_register_device(input_dev);
	if(err)
	  goto exit_free_input;


	gpio_request_one(GPIO_INT, GPIOF_IN, "isl2903");
	//set irq type to edge falling 
	irq_set_irq_type(gpio_to_irq(GPIO_INT), IRQF_TRIGGER_FALLING);

	err = request_irq(gpio_to_irq(GPIO_INT), isl29023_irq_handler, 0, client->dev.driver->name, data);
	if(err < 0){
		printk("bshui failed to register irq \n");
		goto exit_free_input;
	}

	data->workqueue = create_singlethread_workqueue("isl29023");
	INIT_WORK(&data->work, isl29023_work);
	if(data->workqueue == NULL){
		printk("bshui couldn't create workqueue\n'");
		err = -ENOMEM;
		goto exit_free_interrupt;
	}
	printk("bshui isl29023 probe----\n");

	return 0;
exit_free_interrupt:
	free_irq(gpio_to_irq(GPIO_INT), data);
exit_free_input:
	input_free_device(input_dev);
exit_kfree:
	kfree(data);
	return err;
}

static int isl29023_remove(struct i2c_client *client){

	struct isl29023_data *data = i2c_get_clientdata(client);

	cancel_work_sync(&data->work);
	destroy_workqueue(data->workqueue);
	free_irq(gpio_to_irq(GPIO_INT), data);
	input_unregister_device(data->input);
	input_free_device(data->input);
	sysfs_remove_group(&client->dev.kobj, &isl29023_attr_group);
	isl29023_set_power_state(client, 0);
	kfree(i2c_get_clientdata(client));

	printk("bshui isl29023 remove\n");

	return 0;
}

static int isl29023_detect(struct i2c_client *client, struct i2c_board_info *info){
	struct i2c_adapter *adapter = client->adapter;
	int twi_id = 1;
	if(!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
	  return -ENODEV;

	if(twi_id == adapter->nr){
		strlcpy(info->type, ISL29023_NAME, I2C_NAME_SIZE);
		return 0;
	}else
	  return -ENODEV;
}
static struct i2c_driver isl29023_driver = {
	.class = I2C_CLASS_HWMON,
	.driver = {
		.name  = ISL29023_NAME,
		.owner = THIS_MODULE,
	},
	.probe	  = isl29023_probe,
	.remove   = isl29023_remove,
	.id_table = isl29023_id,
	.address_list = normal_i2c,
	.detect   = isl29023_detect,
};

static int __init isl29023_init(void){
	
	return i2c_add_driver(&isl29023_driver);
}

static void __exit isl29023_exit(void){
	i2c_del_driver(&isl29023_driver);
}
module_init(isl29023_init);
module_exit(isl29023_exit);
MODULE_LICENSE("GPL");
